#include "bezier.h"
#include "GL/glu.h"
#include <iostream>

Bezier::Bezier()
{
    dimSpace = 0;
    dimDomain = 0;
    dimCtrlPoly = NULL;
    numCtrlPts = 0;
    ctrlPts = NULL;
}

Bezier::Bezier(const Bezier &b)
{
    dimSpace = b.dimSpace;
    dimDomain = b.dimDomain;

    dimCtrlPoly = new int[dimDomain];
    for (int i = 0; i < dimDomain; i++)
    {
        dimCtrlPoly[i] = b.dimCtrlPoly[i];
    }
    numCtrlPts = b.numCtrlPts;

    ctrlPts = NULL;
    allocateCtrlPts();

    for (int i = 0; i < numCtrlPts; i++)
    {
        for (int j = 0; j < dimSpace; j++)
        {
            ctrlPts[i][j] = b.ctrlPts[i][j];
        }
    }
}

Bezier::Bezier(int _dimDomain, int _dimSpace, int *_ctrlPoly, double **_ctrlPts)
{
    dimSpace = _dimSpace;
    dimDomain = _dimDomain;

    dimCtrlPoly = new int[dimDomain];
    for (int i = 0; i < dimDomain; i++)
    {
        dimCtrlPoly[i] = _ctrlPoly[i];
    }

    numCtrlPts = 1;
    for (int i = 0; i < dimDomain; i++)
    {
        numCtrlPts *= dimCtrlPoly[i];
    }

    ctrlPts = NULL;
    allocateCtrlPts();

    if (_ctrlPts != NULL)
    {
        for (int i = 0; i < numCtrlPts; i++)
        {
            for (int j = 0; j < dimSpace; j++)
            {
                ctrlPts[i][j] = _ctrlPts[i][j];
            }
        }
    }
}

Bezier::~Bezier()
{
    release();
}

double *Bezier::ctrlPt(int *index)
{
    int pos = ctrlPtIndex(index);
    return ctrlPts[pos];
}

void Bezier::ctrlPtIndex(int index, int *pos)
{
    for (int i = 0; i < dimDomain; i++)
    {
        pos[i] = index%dimCtrlPoly[i];
        index /= dimCtrlPoly[i];
    }
}

int Bezier::ctrlPtIndex(int *index)
{
    return ptIndex(index, dimCtrlPoly);
}

int Bezier::getNumOfCtrlPts()
{
    int res = 1;
    for (int i = 0; i < dimSpace; i++)
    {
        res *= dimCtrlPoly[i];
    }
    return res;
}

int Bezier::ptIndex(int *index, int *dim)
{
    int pos = 0;

    for (int i = dimDomain - 1; i >= 0; i--)
    {
        if (index[i] >= dim[i])
        {
            return -1;
        }
        else
        {
            pos += index[i];
            if (i > 0)
            {
                pos *= dim[i - 1];
            }
        }
    }

    return pos;
}

double *Bezier::getPt(int *index, int *dim, double **pts)
{
    int pos = ptIndex(index, dim);
    if (pos >= 0)
    {
        return pts[pos];
    }
    else
    {
        return NULL;
    }
}

void Bezier::casteljau(int domainAxis, double t, Bezier *subB1, Bezier *subB2)
{
    for (int i = 0; i < numCtrlPts; i++)
    {
        for (int j = 0; j < dimSpace; j++)
        {
            subB1->ctrlPts[i][j] = ctrlPts[i][j];
        }
    }

    int *pos = new int[dimDomain];
    int *preCol = new int[dimDomain];

    for (int i = 0; i < numCtrlPts; i++)
    {
        ctrlPtIndex(i, pos);
        ctrlPtIndex(i, preCol);

        int j = pos[domainAxis];

        if (j != 0)
        {
            for (int k = dimCtrlPoly[domainAxis] - 1; k >= j; k--)
            {
                pos[domainAxis] = k;
                preCol[domainAxis] = k-1;
                for (int axis = 0; axis < dimSpace; axis++)
                {
                    subB1->ctrlPts[ctrlPtIndex(pos)][axis] = subB1->ctrlPts[ctrlPtIndex(preCol)][axis] * (1-t)
                                                   + subB1->ctrlPts[ctrlPtIndex(pos)][axis] * t;
                }
            }
        }

        pos[domainAxis] = dimCtrlPoly[domainAxis] - 1 - j;
        preCol[domainAxis] = dimCtrlPoly[domainAxis] - 1;

        for (int axis = 0; axis < dimSpace; axis++)
        {
            subB2->ctrlPts[ctrlPtIndex(pos)][axis]
                    = subB1->ctrlPts[ctrlPtIndex(preCol)][axis];
        }
    }

    delete []pos;
    delete []preCol;
}

Bezier &Bezier::operator =(const Bezier &b)
{
    release();

    dimSpace = b.dimSpace;
    dimDomain = b.dimDomain;

    dimCtrlPoly = new int[dimDomain];
    for (int i = 0; i < dimDomain; i++)
    {
        dimCtrlPoly[i] = b.dimCtrlPoly[i];
    }
    numCtrlPts = b.numCtrlPts;

    for (int i = 0; i < numCtrlPts; i++)
    {
        for (int j = 0; j < dimSpace; j++)
        {
            ctrlPts[i][j] = b.ctrlPts[i][j];
        }
    }
}

void Bezier::allocateCtrlPts()
{
    if (ctrlPts)
    {
        releaseCtrlPts(ctrlPts, numCtrlPts);
    }

    ctrlPts = new double*[numCtrlPts];

    for (int i = 0; i < numCtrlPts; i++)
    {
        ctrlPts[i] = new double[dimSpace];
    }
}

double** Bezier::allocateCtrlPts(int num)
{
    double **ctrlPts;

    ctrlPts = new double*[num];

    for (int i = 0; i < num; i++)
    {
        ctrlPts[i] = new double[dimSpace];
    }

    return ctrlPts;
}

void Bezier::releaseCtrlPts()
{
    if (ctrlPts)
    {
        for (int i = 0; i < numCtrlPts; i++)
        {
            delete []ctrlPts[i];
        }

        delete []ctrlPts;
    }

    ctrlPts = NULL;
}

void Bezier::releaseCtrlPts(double **ctrlPts, int num)
{
    if (ctrlPts)
    {
        for (int i = 0; i < num; i++)
        {
            delete []ctrlPts[i];
        }

        delete []ctrlPts;
    }

    ctrlPts = NULL;
}

void Bezier::release()
{
    if (dimCtrlPoly)
    {
        delete []dimCtrlPoly;
    }

    releaseCtrlPts();
}
